![]() |
![]() |
|
Die Main-Methode, der Einstiegspunkt der Laufzeit einer Konsolenanwendung, ist das beste Beispiel dafür:
Dem Methodennamen folgt in runden Klammern optional eine Parameterliste, um gegebenenfalls dem Methodenaufruf Daten zu übergeben, welche die Methode zur Ausführung benötigt. Ist die Liste leer, wie auch in GetFlaeche und GetUmfang, dürfen die Klammern nicht fehlen. Dem Methodenkopf folgt der Anweisungsblock, in dem die Funktionalität der Methode implementiert ist. Abgeschlossen wird die Methode mit End Sub. Mit Exit Sub wird die Ausführung vorzeitig abgebrochen. Das Programm verzweigt in beiden Fällen zurück in die Zeile, die dem Methodenaufruf folgt. Anstatt mit Exit Sub die Methode zu verlassen, bietet sich auch Return an. 4.3.3 Der Aufruf einer Methode
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Imports CircleApplication |
| Module Module1 |
| Sub Main() |
| ' Objekt vom Typ Circle erzeugen und dem Radius |
| ' einen Wert zuweisen |
| Dim meinKreis As Circle = New Circle() |
| meinKreis.Radius = 12 |
| ' Testen der Methoden GetFlaeche |
| Dim area As Double = meinKreis.GetFlaeche() |
| Console.WriteLine("Fläche = {0}", area) |
| ' Testen der Methode GetUmfang |
| Console.WriteLine("Umfang = {0}", meinKreis.GetUmfang()) |
| Console.ReadLine() |
| End Sub |
| End Module |
Vergessen Sie nicht, den Namespace CircleApplication, in dem die Klasse Circle definiert ist, mit Imports anzugeben.
Bisher haben wir nur parameterlose Methoden berücksichtigt. Viele Methoden benötigen jedoch Dateninformationen, die den Ablauf oder die Steuerung der Operation entscheidend beeinflussen. Diese Daten werden der Methode beim Aufruf als Argumente übergeben und von der Methode in der Parameterliste empfangen.
Angenommen in der Klasse Circle wäre eine Methode namens GetFlaeche definiert, die nicht auf das Feld Radius zur Flächenberechnung zurückgreift. In diesem Fall muss beim Aufruf der Methode der Radius als Argument übergeben werden. Sehen wir uns zunächst die parametrisierte Methode GetFlaeche an:
| Public Function GetFlaeche(ByVal radius As Double) As Double |
| Dim flaeche As Double = 2 * 3.14 * radius |
| Return flaeche |
| End Function |
Die Deklaration eines Parameters in der Methodendefinition erinnert an die Deklaration einer Variablen: Zuerst wird der Bezeichner des Parameters angegeben, danach folgt der Datentyp. GetFlaeche definiert also einen Parameter namens radius vom Typ Double. Zu beachten ist die Sichtbarkeit des Parameters: Er ist nur innerhalb der Methode bekannt. Damit ist seine Lebensdauer auf die Ausführungszeit der Methode beschränkt.
Nun wollen wir auch die parametrisierte Methode GetFlaeche testen:
| Dim meinKreis As Circle = New Circle() |
| Dim kreisradius As Double = 4.5 |
| Dim area As Double = meinKreis.GetFlaeche(kreisradius) |
GetFlaeche setzt ein konkretes Objekt voraus. Deshalb muss zuerst die Klasse Circle instanziiert werden. Danach wird auf die Objektreferenz meinKreis die Methode GetFlaeche aufgerufen, wobei in den runden Klammern das Argument übergeben wird, das hier die Variable kreisradius mit dem Inhalt 4,5 ist. Die Methode nimmt das Argument im Parameter radius entgegen, der nun seinerseits ebenfalls den Inhalt 4,5 hat.
In einer Parameterliste können beliebig viele Parameter definiert werden. Das folgende Codefragment beschreibt die Klasse Rectangle mit der Methode GetRectFlaeche. Die Parameterliste dieser Methode definiert die beiden Parameter laenge und breite, die durch ein Komma voneinander getrennt werden. Aus den an diese Parameter übergebenen Werten wird die Grundfläche des Rechtecks berechnet und als Resultat des Methodenaufrufs geliefert:
| Class Rectangle |
| Public Function GetRectFlaeche(ByVal laenge As Double, _ |
| ByVal breite As Double) |
| Dim flaeche As Double = laenge * breite |
| Return flaeche |
| End Function |
| End Class |
Der Aufruf der Methode könnte folgendermaßen lauten:
| Dim rechteck As Rectangle = New Rectangle() |
| Dim flaeche As Double = rechteck.GetRectFlaeche(3, 6) |
Anstelle eines Literals können Sie auch Variablennamen angeben, die zur Laufzeit durch die entsprechenden Werte ersetzt werden:
| Dim rechteck As Rectangle = New Rectangle() |
| Dim x As Double = 3.4 |
| Dim y As Double = 6.19 |
| Dim flaeche As Double = rechteck.GetRectFlaeche(x, y) |
Bei Methoden, die mehr als einen Parameter erwarten, müssen Sie immer die Reihenfolge der übergebenen Argumente beachten: Das erste Argument wird dem ersten Parameter zugewiesen, das zweite Argument dem zweiten Parameter usw.
Methoden mit Rückgabewert sind der einfachste Weg, um zwischen einer Methode und ihrem Aufrufer Daten auszutauschen. Dabei wird ein Methodenaufruf wie ein Variablenname bewertet, da die Methode einen bestimmten Wert repräsentiert. So wäre es möglich, einen Methodenaufruf in einem Ausdruck als Operand einzusetzen, wie das folgende Beispiel zeigt, das die Methode GetFlaeche der Circle-Klasse dazu benutzt, um das Volumen eines Zylinders zu berechnen:
| Circle kreis = New Circle() |
| Dim hoehe As Double = 3 |
| Dim volumen As Double = kreis.GetFlaeche(10.7) * hoehe |
Einer Methode mit Rückgabewert muss gesagt werden, um welchen es sich handelt. Dazu dient das Schlüsselwort Return, hinter dem der Rückgabewert angegeben wird. Es ist nicht unbedingt notwendig, zuerst das Ergebnis einer Operation einer lokalen Variablen zuzuweisen. Die Operation darf auch direkt hinter Return erfolgen:
| Public Function GetFlaeche() As Double |
| Return 3.14 * Math.Pow(Radius, 2) |
| End Function |
Sobald das Return-Statement erreicht wird, kehrt die Programmausführung zum aufrufenden Code zurück. Alle Anweisungen, die einem Return folgen, werden nicht mehr ausgeführt.
Handelt es sich bei einer Methode um eine Function, muss im Anweisungsblock in jedem Fall Return enthalten und erreichbar sein, da der VB-Compiler die Kompilierung ansonsten mit einem Warnhinweis quittiert. Das wäre beispielsweise der Fall, wenn in der folgenden Methode dem Parameter die Zahl 3 übergeben wird. Der VB-Compiler ist intelligent genug, um das zu erkennen, und informiert Sie mit einem entsprechenden Hinweis.
| Public Function MyMethod(ByVal x As Integer) As String |
| If (x < 3) Then |
| Return "Variable ist kleiner als 3" |
| ElseIf (x > 3) Then |
| Return "Variable ist größer als 3" |
| End If |
| End Function |
Die Return-Anweisung ist nicht nur auf Methoden mit Rückgabewert beschränkt. Auch Sub-Methoden können damit vorzeitig verlassen werden. Bei diesen ist die Angabe von Return jedoch optional.
Der Rückgabewert einer Methode muss nicht unbedingt vom Aufrufer entgegengenommen werden – er kann ihn auch ignorieren. Daher ist der folgende Aufruf der Methode GetFlaeche absolut zulässig, wenn auch in diesem Fall wertlos:
| kreis.GetFlaeche() |
Wir wollen uns nun ein Beispiel ansehen, in dem der Rückgabewert einer Methode ein Array ist.
| ' ------------------------------------------------------------ |
| ' Beispiel:...\Kapitel 4\ArrayRueckgabe |
| ' ------------------------------------------------------------ |
| Module Module1 |
| Sub Main() |
| Dim obj As New TestClass |
| Dim arr() As Int32 |
| arr = obj.MyFunc() |
| For i As Integer = 0 To arr.Length – 1 |
| Console.WriteLine(arr(i)) |
| Next |
| Console.ReadLine() |
| End Sub |
| End Module |
| Class TestClass |
| Public Function MyFunc() As Int32() |
| Dim intArr() As Int32 = {2, 4, 6, 8} |
| Return intArr |
| End Function |
| End Class |
Soll eine Methode ein Array an den Aufrufer zurückliefern, muss dies in der Methodensignatur bekannt gegeben werden. In MyFunc wird das lokale Array intArr mit Daten gefüllt und dient als Argument der Return-Anweisung:
| Return intArr |
Das ist vollkommen ausreichend, denn ein Array ist auch ein Objekt und der Name eines Arrays die Referenz auf den vom Array reservierten Speicherbereich. Der Aufrufer nimmt die Rückgabe mit
| arr = obj.MyFunc() |
im Array arr in Empfang. Das Array arr ist damit initialisiert. Anschließend kann in üblicher Weise über die Indizes auf die einzelnen Elemente des Arrays zugegriffen werden.
Viele Methoden enthalten einen eigenen Satz von Variablen. Variablen, die innerhalb des Anweisungsblocks einer Methode deklariert sind, gelten als lokale Variablen. In der folgenden Methode handelt es sich um die Variable lngVariable.
| Public Sub MyMethod() |
| Dim lngVariable As Long = 34 |
| ' Anweisungen |
| End Sub |
Lokale Variablen sind nur in der Methode sichtbar, in der sie deklariert sind. Programmcode, der sich außerhalb der Methode befindet, kann lokale Variablen weder sehen noch manipulieren oder auswerten. Das gilt auch für Aufrufverkettungen, wenn beispielsweise aus der Methode heraus eine zweite und aus dieser heraus wieder eine dritte Methode aufgerufen wird.
Lokale Variablen sind mit ihrem typspezifischen Standardwert vorinitialisiert.
Felder und lokale Variablen können durchaus gleichnamig sein, wie das folgende Codefragment zeigt:
| Class ClassA |
| Public myValue As Integer |
| Public Sub Method1() |
| Dim myValue As Integer = 10 |
| myValue = 4711 |
| End Sub |
| Public Sub Method2() |
| myValue = 25 |
| End Sub |
| End Class |
ClassA enthält das Feld myValue, derselbe Bezeichner wurde in der Methode Method1 für eine lokale Variable gewählt. Eine Anweisung in Method1 wie beispielsweise
| myValue = 4711 |
verändert den Inhalt der Variablen, deren Gültigkeitsbereich der der Anweisung am nächsten stehende ist – in diesem Fall wird also der Inhalt der lokalen Variablen geändert und nicht das gleichnamige Feld auf Klassenebene. Soll in Method1 aber das gleichnamige, auf Klassenebene deklarierte Feld angesprochen werden, muss dem Feldnamen das Schlüsselwort Me vorausgehen, z. B.:
| Me.myValue = 245 |
Method2 manipuliert ebenfalls myValue. Da in Method2 die lokale Variable myValue der Methode Method1 unbekannt ist, wird der Wert direkt dem Feld zugewiesen. Es wäre aber nicht falsch, trotzdem Me zu verwenden.
|
Me ist ein Schlüsselwort, das auf die aktuelle Instanz verweist, und bietet sich an, um auf Methoden oder Eigenschaften des aktuellen Objekts zuzugreifen. |
Zugriffsmodifizierer beschreiben die Sichtbarkeit eines Elements. Wie Sie bereits gelernt haben, kann eine Klasse nur Public oder Friend sein. In gleicher Weise werden aber auch die Sichtbarkeit und damit der Zugriff auf die Mitglieder einer Klasse festgeschrieben, zu denen unter anderem auch die Ihnen schon bekannten Felder und Methoden gehören. Den beiden bekannten Modifizierern gesellen sich noch weitere hinzu, die Sie der folgenden Tabelle entnehmen können.
| Zugriffsmodifizierer | Beschreibung |
| Public | Der Zugriff unterliegt keinerlei Einschränkungen. |
| Private | Der Zugriff auf ein als Private definiertes Mitglied ist nur innerhalb der Klasse möglich, in der das private Member definiert ist. Alle anderen Klassen sehen private Member nicht. Deshalb ist darauf auch kein Zugriff möglich. |
| Protected | Der Zugriff auf Protected Member ähnelt dem auf Private definierte. Die Sichtbarkeit ist in gleicher Weise eingeschränkt, jedoch werden Protected Mitglieder an abgeleitete Klassen vererbt. |
| Friend | Die Zugriff auf Friend Member ist nur aus den Klassen heraus gestattet, die sich in derselben Assembly befinden. |
|
Die Angabe eines Zugriffsmodifizierers ist optional. Wird darauf verzichtet, gilt das Klassenmitglied als Public deklariert. |
Schauen Sie sich das folgende Codefragment an:
| Sub Main() |
| Dim intVar As Integer = 3 |
| Console.WriteLine("intVar in Main() vorher = {0}", intVar) |
| TestProc(intVar) |
| Console.WriteLine("intVar in Main() nachher = {0}", intVar) |
| Console.ReadLine() |
| End Sub |
| Public Sub TestProc(ByVal intPara As Integer) |
| intPara = 550 |
| Console.WriteLine("intPara in TestProc = {0}", intPara) |
| End Sub |
In Main wird die Variable intVar deklariert und mit dem Wert 3 initialisiert. Nach der Ausgabe des Variabeleninhalts an der Konsole wird die Methode TestProc aufgerufen, der als Argument die lokale Variable intVar übergeben wird. TestProc nimmt das Argument in ihrem Parameter intPara entgegen. In der ersten Anweisung innerhalb der Methode wird der Inhalt des Parameters in 550 geändert und gleichfalls an der Konsole ausgegeben. Mit End Sub wird danach die Methode verlassen und die Kontrolle des Programmflusses wieder an Main übergeben, die ihrerseits noch einmal den Inhalt ihrer lokalen Variablen intVar an der Konsole anzeigt. Die Ausgabe wird lauten:
| intVar in Main() nachher = 3 |
Festzuhalten bleibt, dass sich der Inhalt der in Main deklarierten Variablen intVar auch nach dem Aufruf der Methode nicht verändert hat. Der Grund für diese Tatsache – die Sie vielleicht gar nicht überrascht hat – ist in der Definition der Parameterliste zu finden. Hier steht, von uns bislang stillschweigend hingenommen, das Schlüsselwort ByVal.
In Visual Basic gibt es aber auch noch ein weiteres Schlüsselwort, bei dessen Einsatz die letzte Konsolenausgabe eine andere sein wird: ByRef. Eine Änderung der Methodensignatur in
| Public Sub TestProc(ByRef intPara As Integer) |
wird an der Eingabeaufforderung zu folgender Ausgabe führen:
| intVar in Main() vorher = 3 |
| intPara in TestProc = 550 |
| intVar in Main() nachher = 550 |
Die Änderung der Parameterdefinition von ByVal in ByRef hat also ganz eindeutig Einfluss auf das übergebene Argument intVar ausgeübt – intVar hat nach dem Methodenaufruf genau den Inhalt angenommen, der dem Parameter intPara zugewiesen worden ist.
Um zu verstehen, was sich hinter den Kulissen abspielt, müssen wir einen Blick in den Teilbereich des Speichers werfen, in dem die Programmdaten vorgehalten werden.

Hier klicken, um das Bild zu Vergrößern
Abbildung 4.10 Parameterübergabe als Referenz (Call by Reference)
Zunächst wird bei der Deklaration der Variablen intVar Speicher allokiert. Nehmen wir rein hypothetisch an, es sei die Speicheradresse 10046 – so wie in der Abbildung 4.10 dargestellt ist. In diese Speicherzelle (genau genommen sind es natürlich 4 Byte, die ein Integer für sich beansprucht) wird der Wert 3 geschrieben.
Ein Parameterbezeichner ist nichts anderes als eine lokale Variable. Genau das ist der entscheidende Punkt, denn somit ist ein Parameter auch nichts anderes als der Platzhalter einer Adresse im Speicher.
Anhand des Schlüsselwortes ByRef erkennt die aufgerufene Methode TestProc, dass intPara Platzhalter für dieselbe Adresse sein soll, in der das Datum des übergebenen Arguments vorgehalten wird:
Adresse von intPara = Adresse von intVar
In unserem Beispiel beschreibt intPara folglich ebenfalls den Inhalt der Adresse 10046. Diese Speicheradresse ist damit zur Laufzeit unter zwei Namen bekannt: Unter intVar und intPara. Ändert die Methode TestProc den Inhalt von intPara in 550, wird das geänderte Datum in die Speicheradresse 10046 geschrieben.
Nachdem der Programmablauf wieder an die aufrufende Methode zurückgegeben worden ist, wird an der Konsole der Inhalt der Variablen intVar – also der Inhalt, der unter der Adresse 10046 zu finden ist – angezeigt: Es handelt sich um den Wert 550.
Zusammenfassend sei festgestellt, dass die Deklaration eines Parameters mit ByRef die Übergabe einer Speicheradresse bewirkt. Weil sowohl die aufrufende als auch die aufgerufene Methode mit derselben Adresse arbeiten, wirken sich Änderungen, die in der aufgerufenen Methode an dem Parameter vorgenommen werden, auch in der aufrufenden aus. Diese Technik der Parameterübergabe wird daher als Referenzübergabe (engl.: Call by Reference) bezeichnet.
Kommen wir noch einmal zum Eingangsbeispiel zurück. Der Parameter wurde mit dem Schlüsselwort ByVal deklariert:
| Public Sub TestProc(ByVal iPara As Integer) |
Wie Sie gesehen haben, wird der Inhalt des übergebenen Arguments aus Sicht der aufrufenden Methode Main nicht verändert.
Mit der Übergabe des Arguments intVar wird von der Methode TestProc zunächst Speicher allokiert – in der folgenden Abbildung 4.11 durch die fiktive Adresse 10050 beschrieben. Danach wird der Inhalt des Arguments intVar – also 3 – in die Speicherzelle 10050 kopiert.

Hier klicken, um das Bild zu Vergrößern
Abbildung 4.11 Parameterübergabe als Wertübergabe (Call by Value)
Ändert TestProc den Inhalt von intPara, wird die Änderung folglich in die Adresse 10050 geschrieben und nicht in die Originaladresse 10046 des übergebenen Arguments intVar. Damit weisen die beiden in unserem Beispiel angenommenen Adressen die folgenden Inhalte auf:
Adresse 10046 = 3
Nachdem der Programmablauf wieder zu der aufgerufenen Methode zurückgekehrt ist, wird der Inhalt der Variablen intVar, der sich diesmal nicht verändert hat, an der Konsole ausgegeben: Es ist weiterhin die Zahl 3. Diese Technik der Argumentübergabe wird als Wertübergabe (engl.: Call by Value) bezeichnet.
Die Wertübergabe mit ByVal ist der Standard unter Visual Basic 2005.
Vergessen Sie, entweder ByRef oder ByVal ausdrücklich anzugeben, wird die Entwicklungsumgebung die Standardbeschreibung automatisch einfügen.
In allen bisherigen Ausführungen haben wir der Parameterliste nur einfache Datentypen wie Intger oder Long übergeben. .NET ordnet alle Datentypen zwei Gruppen zu: entweder den Werte- oder den Referenztypen. Zu den Wertetypen gehören beispielsweise Boolean, Byte, Integer, Double usw., zu den Referenztypen alle Typen, die auf einer Klassendefinition basieren. Sie werden im nächsten Kapitel noch erfahren, dass Wertetypen nicht als Klassen, sondern als Strukturen definiert sind.
Selbstverständlich können wir auch Parameter deklarieren, denen ein Referenztyp zugrunde liegt. Dazu ein Beispiel:
| Module Module1 |
| Sub Main() |
| Dim clsA As ClassA = New ClassA() |
| Dim clsB As ClassB = New ClassB() |
| clsB.ChangeObject(clsA) |
| Console.WriteLine(clsA.TestValue) |
| Console.ReadLine() |
| End Sub |
| End Module |
| Class ClassA |
| Public TestValue As Integer = 500 |
| End Class |
| Class ClassB |
| Public Sub ChangeObject(ByVal obj As ClassA) |
| obj.TestValue = 4711 |
| End Sub |
| End Class |
Hier sind die beiden Klassen ClassA und ClassB definiert. ClassB hat eine Methode, der im Parameter obj ein Objekt vom Typ ClassA übergeben wird. In der Methode wird das Feld TestValue des ClassA-Objekts manipuliert.
In Main wird je ein Objekt der beiden Klassen erzeugt. Dem Aufruf der Methode ChangeObject des ClassB-Objekts wird das Objekt vom Typ ClassA übergeben. Nach dem Methodenaufruf wird an der Konsole der Inhalt des Feldes TestValue des ClassA-Objekts angezeigt – es ist der Wert 4711. Das mag im ersten Moment erstaunen, denn wir haben es in diesem Beispiel mit einem Wertparameter zu tun. Nach eingehender Überlegung wird man aber zu der Erkenntnis kommen, dass eine Objektvariable ein Verweis auf eine Speicheradresse ist, unter der das Objekt zu finden ist. Der Verweis wird in den Parameter kopiert und arbeitet dann konsequente mit dem Originalobjekt.
Wir wollen nun eine Methode definieren, welche die Fläche eines Rechtecks anhand der ihr übergebenen Daten des Rechtecks ermittelt. Der Name der Funktion soll lauten GetRectangleArea. Diese Aufgabe erscheint im ersten Moment trivial, aber bei einer weitergehenden Analyse werden Sie sich die Frage stellen, wie die Daten aussehen, die von der Funktion entgegengenommen werden. Ist es das Längenmaß der beiden Seiten, also Breite und Höhe, oder sollen die beiden das Rechteck beschreibenden Eckpunkte mitgeteilt werden, aus denen die Fläche des Rechtecks berechnet wird? Insbesondere dann, wenn Sie eine Methode entwickeln, die nicht nur der eigenen Anwendung zugute kommen, sondern auch anderen Anwendungen ihre Dienste bereitstellen soll, sollten zumindest diese beiden Fälle berücksichtigt werden.
Nach dem bisherigen Kenntnisstand müssten Sie zwei Methoden mit unterschiedlichen Bezeichnern implementieren, also beispielsweise GetRectangleAreaA und GetRectagleAreaB.
| 'Funktion zur Übergabe des Breiten- und Höhenmaßes |
| Public Function GetRectangleAreaA(ByVal breite as Single, _ |
| ByVal laenge As single) As Double |
| 'Funktion zur Übergabe der Eckpunktkoordinaten |
| Public Function GetRectangleAreaB(ByVal x1 as Single, _ |
| ByVal y1 As Single, _ |
| ByVal x2 As single, _ |
| ByVal y2 As Single) As Double |
Dass dies keine gute Lösung ist, liegt auf der Hand. Denn das Problem, das unsere fiktive Funktion GetRectangleArea aufzeigt, nämlich für jede anders lautende Parameterliste einen anderen Methodenbezeichner festzulegen, wird bei anderen Methoden, die noch weitaus mehr voneinander abweichende Parameterlisten zulassen müssen, zu einer schier unüberschaubaren Fülle verschiedener Methodennamen führen.
Objektorientierte Sprachen bieten eine sehr einfache und effektive Problemlösung an: die Methodenüberladung. Von einer Überladung wird dann gesprochen, wenn sich zwei oder mehr gleichnamige Methoden nur in der Parameterliste unterscheiden. Die Unterscheidung anhand des Rückgabetyps ist nicht zulässig und führt zu einer Fehlermeldung.
In Visual Basic können überladene Methoden optional durch die Angabe des Schlüsselworts Overloads gekennzeichnet werden. Die Signaturen unserer Methode zur Berechnung der Rechteckfläche könnten also wie folgt definiert werden:
| Public Overloads Function GetRectangleArea(ByVal breite as Single, ...) As Double |
und
| Public Overloads Function GetRectangleArea(ByVal x1 as Single, ...) As Double |
Der Modifizierer Overloads ist optional. Sobald Sie Overloads jedoch in einer der Deklarationen verwenden, müssen Sie es in allen verwenden.
Als alleiniges Kriterium einer überladenen Methode gilt die Parameterliste. Eine Überladung liegt auch dann nicht vor, wenn – eine identische Parameterliste vorausgesetzt – eine gleichnamige Sub- und Function-Methode definiert wird.
Stellen Sie sich vor, Sie beabsichtigen, eine Methode zu entwickeln, um Zahlen zu addieren. Eine Addition ist nur dann sinnvoll, wenn aus zwei Zahlen die Summe gebildet wird. Daher definieren Sie eine Funktionssignatur wie die folgende:
| Public Function Add(ByVal Summand1 As Single, _ |
| ByVal Summand2 As Single) As Single |
Vielleicht haben Sie danach noch die Idee, nicht nur zwei Zahlen, sondern drei bzw. vier zu addieren. Da Sie wissen, dass Visual Basic die Methodenüberladung unterstützt, ergänzen Sie den Programmcode durch zwei entsprechend überladene Funktionen.
| Public Function Add(ByVal Summand1 As Single, _ |
| ByVal Summand2 As Single, _ |
| ByVal Summand3 As Single) As Single |
und
| Public Function Add(ByVal Summand1 As Single, _ |
| ByVal Summand2 As Single, _ |
| ByVal Summand3 As Single, _ |
| ByVal Summand4 As Single) As Single |
Wenn Ihnen dieser Ansatz kritiklos gefällt, sollten Sie sich mit der Frage auseinander setzen, wie viele überladene Funktionen Sie maximal zu schreiben bereit sind, wenn möglicherweise nicht nur vier, sondern 10, 25 oder beliebig viele Zahlen addiert werden sollen.
Es muss für diese Problemstellung eine bessere Lösung geben – und es gibt sie auch: Sie definieren eine Parameterliste mit optionalen Parametern. Bisher haben wir alle Parameter so definiert, dass jedem Parameter beim Aufruf der Methode ein Argument übergeben werden musste. Nun lernen Sie Parameter kennen, denen ein Argument übergeben werden kann – aber nicht muss.
Um einen Parameter zu einem optionalen zu machen, muss vor dem Bezeichner das Schlüsselwort Optional angegeben werden. Dabei ist aber Folgendes zu beachten:
| Regel 1 |
|
Einem optionalen Parameter darf nur ein weiterer optionaler folgen. Enthält eine Parameterliste sowohl nichtoptionale als auch optionale, stehen alle nichtoptionalen vor dem ersten optionalen. |
Mit dieser Erkenntnis soll die Methode Add nun so ergänzt werden, dass nicht nur zwei, sondern maximal vier Zahlen summiert werden können. Die ersten beiden Parameter werden weiterhin in bekannter Weise statisch deklariert. Ihnen muss ein Argument übergeben werden, der dritte und vierte Parameter stehen im Bedarfsfall zur Verfügung:
| Public Function Add(ByVal Summand1 As Single, _ |
| ByVal Summand2 As Single, _ |
| Optional ByVal Summand3 As Single = 0, _ |
| Optional ByVal Summand4 As Single = 0) As Single |
Bei der Definition optionaler Parameter in einer Parameterliste ist eine weitere wichtige Regel zu beachten:
| Regel 2 |
|
Optionalen Parametern muss ein Standardwert mitgeteilt werden, der sich der Typdeklaration des Parameters anschließt. |
Die Angabe eines Standardwertes erlaubt die einfache Bestimmung, ob der aufrufende Code dem optionalen Parameter ein Argument mitteilt und dieses vom deklarierten Standardwert abweicht oder nicht. Mit einer Bedingungsprüfung lässt sich das sehr einfach feststellen:
| If Summand3 <> 0 Then ... |
Im Beispiel zur Addition von maximal vier Zahlen kann sogar auf eine Prüfung ganz verzichtet werden. Mit
| Return Summand1 + Summand2 + Summand3 + Summand4 |
wird in jedem Fall das korrekte Ergebnis an den Aufrufer zurückgegeben, weil aufgrund der passend gewählten Standardwerte die Summe nicht davon beeinflusst wird, ob dem dritten und/oder vierten Parameter ein Wert übergeben wird. Die so definierte Funktion Add() kann mit auf verschiedene Weisen aufgerufen werden:
| sngVar = Add(Arg1, Arg2) |
| sngVar = Add(Arg1, Arg2, Arg3) |
| sngVar = Add(Arg1, Arg2, Arg3, Arg4) |
| sngVar = Add(Arg1, Arg2, , Arg4) |
Beachten Sie insbesondere die letzte Variante. Dem dritten Parameter der Funktion wird kein Wert übergeben, dafür jedoch dem vierten. Auf die Programmlogik der Methode hat das im vorliegenden Fall keinen Einfluss, es könnte ebenso gut das vierte Argument an der dritten Position übergeben werden:
| sngVar = Add(Arg1, Arg2, Arg4) |
Bei anderen Methoden ist die Positionierung aber sehr wohl von entscheidender Bedeutung. Ein Parameter beschreibt häufig einen Zustand und gibt die Position in der Parameterliste vor, die beim Aufruf unbedingt eingehalten werden muss. Die Zuordnung der Position eines übergebenen Arguments zu der Position des empfangenden Parameters bleibt auch bei optionalen Parametern erhalten.
Stellen Sie sich zur Verdeutlichung eine benutzerdefinierte Methode vor, die eine beliebige algebraische Gleichung dritten Grades beschreibt:
| y = Ax3 + Bx2 + Cx + D |
Die vier Konstanten A, B, C und D werden durch die Parameterliste beschrieben. Um einen hohen Grad an Allgemeingültigkeit zu erreichen, werden die einzelnen Glieder der Gleichung optional deklariert und mit dem Standardwert 0 initialisiert.
| Public Function GleichungGrad3(ByVal x As Integer, _ |
| Optional ByVal A As Single = 0, _ |
| Optional ByVal B As Single = 0, _ |
| Optional ByVal C As Single = 0, _ |
| Optional ByVal D As Single = 0) As Double |
| Return A * x ^ 3 + B * x ^ 2 + C * x + D |
| End Function |
| y = 12x3 – 5 |
für x = 17 zu ermitteln, muss die Methode mit
| dblY = GleichungGrad3(17, 12, , , –5) |
aufgerufen werden. Beachten Sie dabei die direkte Zuordnung eines Arguments zu einem bestimmten Parameter. Das quadratische und das lineare Glied fehlen in der Definition der Gleichung. Da allerdings das Konstantenglied belegt ist und ihm die fünfte Position in der Parameterliste zugeteilt wurde, muss es auch als fünftes Argument übergeben werden. Sowohl das dritte als auch das vierte Argument bleiben ungesetzt.
Eigentlich haben wir das ursprünglich gesetzte Ziel mit der Add-Methode erreicht, nämlich beliebig viele Dezimalzahlen vom Typ Single zu addieren: Es müssen nur entsprechend viele optionale Parameter definiert werden. Das kann im Extremfall zu einer sehr langen, unübersichtlichen Parametersignatur führen, wenn sehr viele Zahlenwerte addiert werden müssen. Ein noch größeres Handicap liegt aber wohl darin, dass – unabhängig von der Anzahl der definierten optionalen Parameter – es immer noch nicht möglich ist, wirklich eine nahezu unbegrenzte Anzahl von Werten zu addieren, denn wir sind immer noch an die Anzahl der definierten, optionalen Parameter gebunden.
Die endgültige Lösung, die uns größtmögliche Flexibilität bei gleichzeitiger Überschaubarkeit bietet, haben wir noch nicht gefunden. Visual Basic 2005 bietet uns diese jedoch auf eine sehr einfache Art und Weise an: Es wird ein Parameter als dynamisches Array mit dem Schlüsselwort ParamArray deklariert. Ein Parameter-Array erlaubt einer Methode, eine unbestimmte Anzahl von Übergabeargumenten zu akzeptieren. Die Argumente werden der Reihenfolge nach in das Parameter-Array geschrieben. Um alle übergebenen Argumente zur Laufzeit des Programms richtig zu verarbeiten, muss daher bei jedem Methodenaufruf innerhalb der Methode die Array-Größe ermittelt werden.
Im folgenden Codefragment erhält Add nun den letzten Schliff. Auf die beiden statischen Parameter wurde bewusst verzichtet, um die Signatur so einfach wie möglich zu halten. Daher muss nicht zwangsläufig beim Aufruf ein Argument übergeben werden.
| Public Function GlgGrad3(ParamArray ByVal value() As Single) As Double |
| Dim Summe As Double |
| For i As Integer = 0 To Ubound(value) |
| Summe += value(i) |
| Next |
| Return Summe |
| End Function |
Weil die Methode mit unterschiedlich vielen Argumenten aufgerufen werden kann, muss die Array-Obergrenze bestimmt werden. Dazu dient UBound, welcher der Name des Arrays als Argument übergeben wird, dessen Indexobergrenze ermittelt werden soll.
Mit ParamArray sind ein paar Regeln verknüpft, die eingehalten werden müssen:
| In der Parameterliste einer Methode darf nur ein Parameter-Array festgelegt werden. |
| Erlaubt die Signatur einer Prozedur weitere Argumente, so ist ein Parameter-Array immer als letztes Element in der Liste zu definieren. |
| In einer Parameterliste dürfen nicht gleichzeitig mit Optional und ParamArray deklarierte Parameter auftreten. |
| Ein Parameter-Array muss grundsätzlich als ByVal deklariert werden und nimmt Daten gleichen Typs entgegen. Müssen dennoch typunterschiedliche Argumente übergeben werden, bietet es sich an, das Array vom Typ Object zu deklarieren. Object ist, wie später noch eingehend erörtert wird, der allgemeinste Typ und Basisklasse aller .NET-Typen. |
| Ein Parameter-Array ist immer eindimensional. |
| Es wird zwischen Methoden mit Rückgabewert (Function) und Methoden ohne Rückgabewert (Sub) unterschieden. |
| Methoden können überladen werden. Ausschlaggebend dafür ist einzig und allein nur die Parameterliste. Überladene Methoden können mit dem Modifizierer Overloads gekennzeichnet werden. |
| Liefert eine Methode dem Aufrufer ein Resultat, wird dieses mit der Anweisung Return bereitgestellt. Nach der Ausführung von Return wird die Kontrolle des Programmablaufs an den Aufrufer zurückgegeben, auch bei Sub-Methoden. |
| Lokale Variablen sind Variablen, die innerhalb einer Methode deklariert und nur in dieser bekannt sind. |
| Parameter sind entweder Wert- oder Referenzparameter (ByVal bzw. ByRef). |
| Parameter, die ByVal übergeben werden, können ohne Auswirkung auf die aufrufende Methode in der aufgerufenen manipuliert werden. Diese Übergabemethode wird als Call by Value bezeichnet. Parameter, die ByRef definiert sind, empfangen die Adresse des Arguments, nicht aber direkt dessen Datum. Ändert die aufgerufene Methode einen ByRef empfangenen Parameter, wirkt sich dies auf den Aufrufer aus. Diese Art der Parameterdefinition wird als Call by Reference bezeichnet. |
| Die Übergabe von Objektreferenzen erfolgt grundsätzlich immer ByRef, selbst dann, wenn der Typ eines solchen Parameters ByVal definiert ist. |
| Argumente können optional übergeben werden. Dazu wird der Parameter mit dem Schlüsselwort Optional definiert. Bei diesen Parametern ist grundsätzlich ein Standardwert anzugeben, den der Parameter annimmt, falls der Aufrufer an der Position des optionalen Parameters kein Argument übergibt. |
| Hinter Optional-Parametern dürfen nur weitere optionale folgen. |
| Um eine unbegrenzte Anzahl optionaler Parameter bereitzustellen, bietet sich die Definition mit ParamArray an. ParamArray-Parameter dürfen nicht zusammen mit optionalen Parametern eingesetzt werden und stehen immer als letztes Glied in der Parameterliste. |
| << zurück |
|
||||||||||||||
|
||||||||||||||
|
||||||||||||||
|
||||||||||||||
Copyright © Galileo Press 2007
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken.
Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die
gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich
geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung,
Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.